package filter.script.pre

import com.alibaba.fastjson.JSONObject
import com.homedo.common.util.Detect
import com.homedo.microservice.open.service.gateway.bean.po.enhanced.RouteEnhanced
import com.homedo.microservice.open.service.gateway.util.ApplicationContextUtil
import com.homedo.microservice.open.service.gateway.zuul.conf.RouteConf
import com.homedo.microservice.open.service.gateway.zuul.constant.ZuulConstant
import com.homedo.microservice.open.service.gateway.zuul.routelocator.LocalRouteLocator
import com.netflix.zuul.ZuulFilter
import com.netflix.zuul.context.Debug
import com.netflix.zuul.context.RequestContext
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.cloud.netflix.zuul.filters.Route
import org.springframework.cloud.netflix.zuul.filters.RouteLocator
import org.springframework.http.HttpHeaders
import org.springframework.web.util.UrlPathHelper

import javax.servlet.http.HttpServletRequest

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.*

public class ZuulHostPreFilter extends ZuulFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZuulHostPreFilter.class);

    private UrlPathHelper urlPathHelper = new UrlPathHelper();
    private RouteLocator routeLocator;

    @Override
    String filterType() {
        return "pre"
    }

    @Override
    int filterOrder() {
        return 6
    }

    @Override
    boolean shouldFilter() {
        return true;
    }

    @Override
    Object run() {
        init();
        RequestContext ctx = RequestContext.getCurrentContext();
        final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
        Route route = this.routeLocator.getMatchingRoute(requestURI);
        if (route != null) {
            RouteEnhanced routeEnhanced = RouteConf.getInstance().getConfs().get(route.getId());
            if (routeEnhanced != null && routeEnhanced.getAddHostHeader() != null && routeEnhanced.getAddHostHeader()) {
                String host = toHostHeader(ctx.getRequest());
                this.resetScheme(ctx);
                this.resetPort(ctx);
                this.resetPrefix(ctx, routeEnhanced);

//                ctx.addZuulRequestHeader(HttpHeaders.HOST, host);
                ctx.addZuulRequestHeader("X-Forwarded-Host", host);

                ctx.put(ZuulConstant.ZUUL_IS_ACCESSED_ZUUL_HOST_PRE_FILTER, true);

                Debug.addRequestDebug("经过ZuulHostPreFilter:[通过Host路由，添加访问来源的Host:" + host + "--" + requestURI + "--" + JSONObject.toJSONString(route) + "]");

//                if (ctx.debugRequest()) {
//                    LOGGER.info("[通过Host路由，添加访问来源的Host:{}--{}--{}]", host, requestURI, JSONObject.toJSONString(route))
//                }
            }
        }
    }

    private String toHostHeader(HttpServletRequest request) {
        String xForwardedHost = request.getHeader("X-Forwarded-Host");
        if (xForwardedHost != null && xForwardedHost.length() > 0) {
            return xForwardedHost;
        }

        int port = request.getServerPort();

        String portStr = request.getHeader("x-forwarded-port");
        if (portStr != null && portStr.length() > 0) {
            port = Integer.valueOf(portStr)
        }

        String scheme = this.getScheme(request);
        if (port == HTTP_PORT && HTTPS_SCHEME.equalsIgnoreCase(scheme)) {
            port = HTTPS_PORT;
        }

        if ((port == HTTP_PORT && HTTP_SCHEME.equalsIgnoreCase(scheme))
                || (port == HTTPS_PORT && HTTPS_SCHEME.equalsIgnoreCase(scheme))) {
            return request.getServerName();
        } else {
            return request.getServerName() + ":" + port;
        }
    }

    private void resetPrefix(RequestContext ctx, RouteEnhanced routeEnhanced) {
        HttpServletRequest request = ctx.getRequest();
        String prefix = request.getHeader("x-forwarded-prefix");
        if (prefix != null && prefix.length() > 0) {
            ctx.addZuulRequestHeader("x-forwarded-prefix", prefix);
            return;
        }

        prefix = routeEnhanced.getPath();
        prefix = prefix.replaceAll("\\*", "");

        boolean stripPrefix = routeEnhanced.getStripPrefix();

        if (routeEnhanced.getAddPrefix() != null && routeEnhanced.getAddPrefix().length() > 0) {
            stripPrefix = true;
        }

        if (!stripPrefix) {
            prefix = "/";
        }

        ctx.addZuulRequestHeader("x-forwarded-prefix", prefix);
    }

    private void resetPort(RequestContext ctx) {
        HttpServletRequest request = ctx.getRequest();
        String portStr = request.getHeader("x-forwarded-port");
        if (portStr != null && portStr.length() > 0) {
            ctx.addZuulRequestHeader("x-forwarded-port", portStr);
            return;
        }

        int port = request.getServerPort();
        String scheme = this.getScheme(request);
        if (port == HTTP_PORT && HTTPS_SCHEME.equalsIgnoreCase(scheme)) {
            port = HTTPS_PORT;
        }

        ctx.addZuulRequestHeader("x-forwarded-port", String.valueOf(port));
    }

    private void resetScheme(RequestContext ctx) {
        HttpServletRequest request = ctx.getRequest();
        String scheme = request.getHeader("x-forwarded-proto");
        if (scheme != null && scheme.length() > 0) {
            ctx.addZuulRequestHeader("x-forwarded-proto", scheme);
        }
    }

    private String getScheme(HttpServletRequest request) {
        String scheme = request.getHeader("x-forwarded-proto");
        if (scheme != null && scheme.length() > 0) {
            return scheme;
        }
        return request.getScheme();
    }

    void init() {
        if (routeLocator == null) {
            synchronized (ZuulHostPreFilter.class) {
                if (routeLocator == null) {
                    routeLocator = ApplicationContextUtil.getApplicationContext().getBean(LocalRouteLocator.class);
                }
            }
        }
    }
}
